1 package edu.jiangxin.apktoolbox.reverse;
2
3 import edu.jiangxin.apktoolbox.swing.extend.plugin.PluginPanel;
4 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
5 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectFileListener;
6 import edu.jiangxin.apktoolbox.utils.Constants;
7 import edu.jiangxin.apktoolbox.utils.Utils;
8 import org.apache.commons.io.FilenameUtils;
9
10 import javax.swing.*;
11 import javax.swing.filechooser.FileFilter;
12 import java.awt.*;
13 import java.awt.event.ActionEvent;
14 import java.awt.event.ActionListener;
15 import java.io.File;
16 import java.io.Serial;
17
18 public class ApktoolPanel extends PluginPanel {
19 @Serial
20 private static final long serialVersionUID = 1L;
21
22 public ApktoolPanel() throws HeadlessException {
23 super();
24 }
25
26 @Override
27 public String getPluginFilename() {
28 return "apktool_2.12.0.jar";
29 }
30
31 @Override
32 public void initUI() {
33 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
34 setLayout(boxLayout);
35
36 JTabbedPane categoryTabbedPane = new JTabbedPane();
37 add(categoryTabbedPane);
38
39 JPanel decodeCategoryPanel = new ApktoolDecodePanel();
40 JPanel rebuildCategoryPanel = new ApktoolRebuildPanel();
41
42 categoryTabbedPane.addTab("Decode", null, decodeCategoryPanel, "Decode the APK file");
43 categoryTabbedPane.setSelectedIndex(0);
44 categoryTabbedPane.addTab("Rebuild", null, rebuildCategoryPanel, "Rebuild the APK file");
45 }
46
47 abstract class ApktoolPanelBase extends JPanel {
48 @Serial
49 private static final long serialVersionUID = 1L;
50
51 protected JPanel srcPanel;
52
53 protected JPanel targetPanel;
54
55 protected JPanel optionPanel;
56
57 protected JPanel operationPanel;
58
59 protected ApktoolPanelBase() throws HeadlessException {
60 super();
61 initUI();
62 }
63
64 private void initUI() {
65 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
66 setLayout(boxLayout);
67
68 createSrcPanel();
69 add(srcPanel);
70 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
71
72 createTargetPanel();
73 add(targetPanel);
74 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
75
76 createOptionPanel();
77 add(optionPanel);
78 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
79
80 createOperationPanel();
81 add(operationPanel);
82 }
83
84 protected void createSrcPanel() {
85 srcPanel = new JPanel();
86 srcPanel.setLayout(new BoxLayout(srcPanel, BoxLayout.X_AXIS));
87 }
88
89 protected void createTargetPanel() {
90 targetPanel = new JPanel();
91 targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
92 }
93
94 protected void createOptionPanel() {
95 optionPanel = new JPanel();
96 optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.X_AXIS));
97 }
98
99 protected void createOperationPanel() {
100 operationPanel = new JPanel();
101 operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
102 }
103 }
104
105 class ApktoolDecodePanel extends ApktoolPanelBase {
106 @Serial
107 private static final long serialVersionUID = 1L;
108
109 private JTextField srcTextField;
110
111 private JTextField targetTextField;
112
113 private JCheckBox resourceIgnoreCheckBox;
114
115 private JCheckBox overrideCheckBox;
116
117 public ApktoolDecodePanel() throws HeadlessException {
118 super();
119 }
120
121 @Override
122 public void createSrcPanel() {
123 super.createSrcPanel();
124
125 srcTextField = new JTextField();
126 srcTextField.setText(conf.getString("apktool.decode.src.file"));
127
128 JButton srcButton = new JButton(bundle.getString("choose.file.button"));
129 srcButton.addActionListener(new SelectFileListener("select a file", srcTextField));
130
131 srcPanel.add(srcTextField);
132 srcPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
133 srcPanel.add(srcButton);
134 }
135
136 @Override
137 public void createTargetPanel() {
138 super.createTargetPanel();
139
140 targetTextField = new JTextField();
141 targetTextField.setText(conf.getString("apktool.decode.target.dir"));
142
143 JButton targetButton = new JButton(bundle.getString("save.dir.button"));
144 targetButton.addActionListener(new SelectDirectoryListener("Save To", targetTextField));
145
146 targetPanel.add(targetTextField);
147 targetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
148 targetPanel.add(targetButton);
149 }
150
151 @Override
152 public void createOptionPanel() {
153 super.createOptionPanel();
154
155 resourceIgnoreCheckBox = new JCheckBox("Ignore res");
156 resourceIgnoreCheckBox.setSelected(false);
157 optionPanel.add(resourceIgnoreCheckBox);
158
159 overrideCheckBox = new JCheckBox("Override");
160 overrideCheckBox.setSelected(true);
161 optionPanel.add(overrideCheckBox);
162 }
163
164 @Override
165 public void createOperationPanel() {
166 super.createOperationPanel();
167
168 JButton decodeButton = new JButton("Decode");
169 decodeButton.addActionListener(new ApktoolDecodePanel.DecodeButtonActionListener());
170
171 operationPanel.add(decodeButton);
172 }
173
174 private final class DecodeButtonActionListener implements ActionListener {
175 @Override
176 public void actionPerformed(ActionEvent e) {
177 String srcPath = checkAndGetFileContent(srcTextField, "apktool.decode.src.file", "Source file is invalid");
178 if (srcPath == null) {
179 return;
180 }
181
182 String targetPath = checkAndGetDirContent(targetTextField, "apktool.decode.target.dir", "Target directory is invalid");
183 if (targetPath == null) {
184 return;
185 }
186
187 String srcBaseName = FilenameUtils.getBaseName(srcPath);
188 StringBuilder sb = new StringBuilder();
189 sb.append(ApktoolPanel.this.getPluginStartupCmd()).append(" d ")
190 .append(srcPath).append(" -o ").append(targetPath).append(File.separator).append(srcBaseName);
191 if (resourceIgnoreCheckBox.isSelected()) {
192 sb.append(" -r");
193 }
194 if (overrideCheckBox.isSelected()) {
195 sb.append(" -f");
196 }
197 Utils.executor(sb.toString(), true);
198 }
199 }
200 }
201
202 class ApktoolRebuildPanel extends ApktoolPanelBase {
203 @Serial
204 private static final long serialVersionUID = 1L;
205
206 private JTextField srcTextField;
207
208 private JTextField targetTextField;
209
210 private JCheckBox signAPK;
211
212 public ApktoolRebuildPanel() throws HeadlessException {
213 super();
214 }
215
216 @Override
217 public void createSrcPanel() {
218 super.createSrcPanel();
219
220 srcTextField = new JTextField();
221 srcTextField.setText(conf.getString("apktool.rebuild.src.dir"));
222
223 JButton srcButton = new JButton(bundle.getString("choose.dir.button"));
224 srcButton.addActionListener(new SelectDirectoryListener("Select Directory", srcTextField));
225
226 srcPanel.add(srcTextField);
227 srcPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
228 srcPanel.add(srcButton);
229 }
230
231 @Override
232 public void createTargetPanel() {
233 super.createTargetPanel();
234
235 targetTextField = new JTextField();
236 targetTextField.setText(conf.getString("apktool.rebuild.target.file"));
237
238 JButton targetButton = new JButton(bundle.getString("save.file.button"));
239 targetButton.addActionListener(new SelectFileListener("save to", targetTextField, new ApktoolRebuildPanel.ApkFileFilter()));
240
241 targetPanel.add(targetTextField);
242 targetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
243 targetPanel.add(targetButton);
244 }
245
246 @Override
247 public void createOptionPanel() {
248 super.createOptionPanel();
249
250 signAPK = new JCheckBox("sign APK");
251 signAPK.setSelected(false);
252 optionPanel.add(signAPK);
253 }
254
255 @Override
256 public void createOperationPanel() {
257 super.createOperationPanel();
258
259 JButton rebuildButton = new JButton("Rebuild");
260 rebuildButton.addActionListener(new ApktoolRebuildPanel.RebuildButtonActionListener());
261 operationPanel.add(rebuildButton);
262 }
263
264 private final class RebuildButtonActionListener implements ActionListener {
265 @Override
266 public void actionPerformed(ActionEvent e) {
267 String srcPath = checkAndGetDirContent(srcTextField, "apktool.rebuild.src.dir", "Source directory is invalid");
268 if (srcPath == null) {
269 return;
270 }
271
272 String targetPath = checkAndGetNewFileContent(targetTextField, "apktool.rebuild.target.file", "Target file is invalid");
273 if (targetPath == null) {
274 return;
275 }
276
277 StringBuilder sb = new StringBuilder();
278 sb.append(ApktoolPanel.this.getPluginStartupCmd()).append(" b ")
279 .append(srcPath).append(" -o ").append(targetPath);
280 Utils.executor(sb.toString(), true);
281 if (signAPK.isSelected()) {
282 sb = new StringBuilder();
283 sb.append(ApktoolPanel.this.getPluginStartupCmd())
284 .append(" -keystore ").append(Utils.getToolsPath()).append(File.separator)
285 .append("debug.keystore").append(" -alias androiddebugkey -pswd android ")
286 .append(targetPath);
287 Utils.executor(sb.toString(), true);
288 }
289 }
290 }
291
292 private final class ApkFileFilter extends FileFilter {
293
294 @Override
295 public boolean accept(File f) {
296 String nameString = f.getName();
297 return nameString.toLowerCase().endsWith(".apk");
298 }
299
300 @Override
301 public String getDescription() {
302 return "*.apk";
303 }
304 }
305 }
306 }
307